use crate::commands::list::ToDoStatus::{Completed, Pending};
use crate::error::TodoError;
use crate::storage::{Status, Storage, TodoItem};
use crate::ExecuteCommand;
use anyhow::Result;
use chrono::{Duration, Local, NaiveDate};
use clap::Parser;
use colored::Colorize;
use std::collections::HashMap;
use std::str::FromStr;

#[derive(Parser, Debug)]
pub struct List {
    /// 指定要查询的天数
    #[arg(short = 'd', long)]
    pub days: Option<u32>,

    /// 指定待办的状态,可选项为 `complete` 和 `pending`, 支持缩写 `c` 和 `p`
    #[arg(short = 's', long,value_parser=parse_status)]
    pub status: Option<ToDoStatus>,
}


#[derive(Debug, Clone)]
pub enum ToDoStatus {
    Completed,
    Pending,
}

impl FromStr for ToDoStatus {
    type Err = TodoError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if "completed".eq_ignore_ascii_case(s) || "c".eq_ignore_ascii_case(s) {
            return Ok(Completed);
        }
        if "pending".eq_ignore_ascii_case(s) || "p".eq_ignore_ascii_case(s) {
            return Ok(Pending);
        }
        Err(TodoError::ParamError("entry `c` or `p`".to_string()))
    }
}

impl From<ToDoStatus> for Status {
    fn from(value: ToDoStatus) -> Self {
        match value {
            Completed => Status::Completed,
            Pending => Status::Pending,
        }
    }
}

fn parse_status(date_str: &str) -> Result<ToDoStatus, TodoError> {
    ToDoStatus::from_str(date_str)
}

impl ExecuteCommand for List {
    fn execute_command(&self, storage: &impl Storage) -> Result<()> {
        let date = subtract_days(self.days.unwrap_or(0))?;

        let status = self.status.clone().map(|s| s.into());

        let todo_list = storage.select(&date, &status)?;

        print_todo_items(&todo_list);

        Ok(())
    }
}

fn print_todo_items(todo_items: &[TodoItem]) {
    // 打印表头
    println!("{:<5} | {:<10} | {:<9} | {:<30}", "ID".cyan(), "Date".cyan(), "Status".cyan(), "Content".cyan());
    println!("{}", "-".repeat(57)); // 打印分隔线


    // Group TodoItems by date while maintaining order
    let mut grouped_by_date: HashMap<NaiveDate, Vec<&TodoItem>> = HashMap::new();
    let mut order_of_dates: Vec<NaiveDate> = Vec::new();

    for item in todo_items {
        if !grouped_by_date.contains_key(&item.date) {
            order_of_dates.push(item.date);
        }
        grouped_by_date.entry(item.date)
            .or_insert_with(Vec::new)  // If the date doesn't exist, insert an empty Vec
            .push(item);               // Push the TodoItem into the Vec for that date
    }

    // Display the result while preserving the order of dates
    for date in order_of_dates {
        if let Some(items) = grouped_by_date.get(&date) {
            items.iter().enumerate()
                .for_each(|(index, item)| {
                    println!("{}", item.format(index == 0))
                })
        }
    }
}

impl TodoItem {
    fn format(&self, is_first_row: bool) -> String {
        format!(
            "{:<5} | {:<10} | {:<9} | {:<30}",
            self.id.to_string().cyan(),
            self.date.format("%Y-%m-%d").to_string().yellow(),
            match self.status {
                1 => "Completed".green(),
                _ => if is_first_row {
                    "Pending".red()
                } else {
                    "Pending".yellow()
                }
            },
            self.content
        )
    }
}

/// 计算从当前日期减去指定天数后的日期
///
/// # Arguments
///
/// * `days` - 一个 u32 类型的参数，表示要减去的天数
///
/// # Returns
///
/// * 返回一个 NaiveDate 表示减去天数后的日期
fn subtract_days(days: u32) -> Result<NaiveDate> {
    // 获取当前日期
    let today = Local::now().date_naive();

    // 减去指定的天数，返回新日期
    today.checked_sub_signed(Duration::days(days as i64)).ok_or(TodoError::UnsupportedTimeRanges().into())
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_subtract_days() {
        let date = subtract_days(3).unwrap();
        println!("{}", date.format("%Y-%m-%d"));
    }
}

